package com.reactim.message.compression;

import com.google.protobuf.MessageLite;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * 消息压缩器
 * 提供Protobuf消息的压缩和解压缩功能
 */
@Slf4j
@Component
public class MessageCompressor {
    
    // 压缩阈值：超过这个大小的消息才进行压缩（字节）
    private static final int COMPRESSION_THRESHOLD = 1024; // 1KB
    
    /**
     * 压缩Protobuf消息
     * @param message Protobuf消息
     * @return 压缩后的字节数组，如果消息太小则返回原始字节数组
     */
    public CompressedMessage compress(MessageLite message) {
        try {
            byte[] originalBytes = message.toByteArray();
            
            // 如果消息小于阈值，不进行压缩
            if (originalBytes.length < COMPRESSION_THRESHOLD) {
                log.debug("消息大小 {} 字节，小于压缩阈值，不进行压缩", originalBytes.length);
                return new CompressedMessage(originalBytes, false, originalBytes.length, originalBytes.length);
            }
            
            // 使用GZIP压缩
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            try (GZIPOutputStream gzipOut = new GZIPOutputStream(baos)) {
                gzipOut.write(originalBytes);
                gzipOut.finish();
            }
            
            byte[] compressedBytes = baos.toByteArray();
            
            // 计算压缩比
            double compressionRatio = (double) compressedBytes.length / originalBytes.length;
            
            log.debug("消息压缩完成: 原始大小={} 字节, 压缩后大小={} 字节, 压缩比={:.2f}%", 
                originalBytes.length, compressedBytes.length, compressionRatio * 100);
            
            return new CompressedMessage(compressedBytes, true, originalBytes.length, compressedBytes.length);
            
        } catch (IOException e) {
            log.error("消息压缩失败", e);
            // 压缩失败时返回原始消息
            byte[] originalBytes = message.toByteArray();
            return new CompressedMessage(originalBytes, false, originalBytes.length, originalBytes.length);
        }
    }
    
    /**
     * 解压缩消息
     * @param compressedMessage 压缩的消息对象
     * @return 解压缩后的字节数组
     */
    public byte[] decompress(CompressedMessage compressedMessage) {
        if (!compressedMessage.isCompressed()) {
            // 未压缩的消息直接返回
            return compressedMessage.getData();
        }
        
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(compressedMessage.getData());
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            
            try (GZIPInputStream gzipIn = new GZIPInputStream(bais)) {
                byte[] buffer = new byte[1024];
                int len;
                while ((len = gzipIn.read(buffer)) != -1) {
                    baos.write(buffer, 0, len);
                }
            }
            
            byte[] decompressedBytes = baos.toByteArray();
            
            log.debug("消息解压缩完成: 压缩大小={} 字节, 解压后大小={} 字节", 
                compressedMessage.getCompressedSize(), decompressedBytes.length);
            
            return decompressedBytes;
            
        } catch (IOException e) {
            log.error("消息解压缩失败", e);
            throw new RuntimeException("消息解压缩失败", e);
        }
    }
    
    /**
     * 压缩字节数组
     * @param data 原始字节数组
     * @return 压缩的消息对象
     */
    public CompressedMessage compressBytes(byte[] data) {
        try {
            // 如果数据小于阈值，不进行压缩
            if (data.length < COMPRESSION_THRESHOLD) {
                log.debug("数据大小 {} 字节，小于压缩阈值，不进行压缩", data.length);
                return new CompressedMessage(data, false, data.length, data.length);
            }
            
            // 使用GZIP压缩
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            try (GZIPOutputStream gzipOut = new GZIPOutputStream(baos)) {
                gzipOut.write(data);
                gzipOut.finish();
            }
            
            byte[] compressedBytes = baos.toByteArray();
            
            // 计算压缩比
            double compressionRatio = (double) compressedBytes.length / data.length;
            
            log.debug("字节数组压缩完成: 原始大小={} 字节, 压缩后大小={} 字节, 压缩比={:.2f}%", 
                data.length, compressedBytes.length, compressionRatio * 100);
            
            return new CompressedMessage(compressedBytes, true, data.length, compressedBytes.length);
            
        } catch (IOException e) {
            log.error("字节数组压缩失败", e);
            // 压缩失败时返回原始数据
            return new CompressedMessage(data, false, data.length, data.length);
        }
    }
    
    /**
     * 获取压缩统计信息
     */
    public CompressionStats getCompressionStats(CompressedMessage compressedMessage) {
        if (!compressedMessage.isCompressed()) {
            return new CompressionStats(
                compressedMessage.getOriginalSize(),
                compressedMessage.getCompressedSize(),
                1.0,
                0.0
            );
        }
        
        double compressionRatio = (double) compressedMessage.getCompressedSize() / compressedMessage.getOriginalSize();
        double spaceSavingPercentage = (1.0 - compressionRatio) * 100;
        
        return new CompressionStats(
            compressedMessage.getOriginalSize(),
            compressedMessage.getCompressedSize(),
            compressionRatio,
            spaceSavingPercentage
        );
    }
    
    /**
     * 压缩消息数据结构
     */
    public static class CompressedMessage {
        private final byte[] data;
        private final boolean compressed;
        private final int originalSize;
        private final int compressedSize;
        
        public CompressedMessage(byte[] data, boolean compressed, int originalSize, int compressedSize) {
            this.data = data;
            this.compressed = compressed;
            this.originalSize = originalSize;
            this.compressedSize = compressedSize;
        }
        
        public byte[] getData() { return data; }
        public boolean isCompressed() { return compressed; }
        public int getOriginalSize() { return originalSize; }
        public int getCompressedSize() { return compressedSize; }
    }
    
    /**
     * 压缩统计信息
     */
    public static class CompressionStats {
        private final int originalSize;
        private final int compressedSize;
        private final double compressionRatio;
        private final double spaceSavingPercentage;
        
        public CompressionStats(int originalSize, int compressedSize, double compressionRatio, double spaceSavingPercentage) {
            this.originalSize = originalSize;
            this.compressedSize = compressedSize;
            this.compressionRatio = compressionRatio;
            this.spaceSavingPercentage = spaceSavingPercentage;
        }
        
        public int getOriginalSize() { return originalSize; }
        public int getCompressedSize() { return compressedSize; }
        public double getCompressionRatio() { return compressionRatio; }
        public double getSpaceSavingPercentage() { return spaceSavingPercentage; }
        
        @Override
        public String toString() {
            return String.format("CompressionStats{原始大小=%d字节, 压缩后大小=%d字节, 压缩比=%.2f%%, 节省空间=%.2f%%}", 
                originalSize, compressedSize, compressionRatio * 100, spaceSavingPercentage);
        }
    }
}